ceph-模块初始化中 prefork源码解析

Last updated on 9 months ago

在 osd启动流程的代码中有一个 global_init_prefork() 的 函数,发现在很多 模块启动流程都有 这个函数,这和模块对应的进程有关系,所以对这个函数剖析下

image-20220901151643117

具体实现函数都在 Preforker 类中 image-20220901154138047

在先执行是 global_init_prefork(g_ceph_context) 函数

global_init_prefork(g_ceph_context)

prefork 的预初始化

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
int global_init_prefork(CephContext *cct)
{
// 这里 判断是不是 判断着进程是不是 以守护进程方式启动
if (g_code_env != CODE_ENVIRONMENT_DAEMON)
return -1;

const auto& conf = cct->_conf;
if (!conf->daemonize) {
//这里进程的pid 写入文件中
if (pidfile_write(conf) < 0)
exit(1);
//生产了 pid文件后,更改文件权限
if ((cct->get_init_flags() & CINIT_FLAG_DEFER_DROP_PRIVILEGES) &&
(cct->get_set_uid() || cct->get_set_gid())) {
chown_path(conf->pid_file, cct->get_set_uid(), cct->get_set_gid(),
cct->get_set_uid_string(), cct->get_set_gid_string());
}

return -1;
}
//这里 告知大家要 pre_fork??????
cct->notify_pre_fork();
// stop log thread
cct->_log->flush();
cct->_log->stop();
return 0;
}

pidfile_write(conf) 函数i就是把进程的 pid 写进 一个文件里,为什么这么做后面再说

image-20220901162701621

forker.prefork

关键部分,这里主要做的是 fork一个子进程,接下来执行的操作都是由 子进程执行,父进程处于 wait状态,并且这里也对标准输入输出做了些处理

image-20220901163924371

父进程处于等待状态,再执行 setsid 建立新会话

image-20220901164030039

global_init_postfork_start();

该函数重新再把当前进程的pid重新写入到pid文件

为什么创建一个经常需要 建立一个pid文件? 可这里先从 pid 文件创建开始讲

image-20220901173051970

在刚才的 pidfile_write 函数中,有调用了一个 函数 pidfh::open image-20220901171416846

pidfh::open 中调用了 open,创建 文件 image-20220901171924980

随后 获取创建文件的信息保存 Preforker 类中 image-20220901172326150

最后一步比较关键,用 flock 给文件上锁

image-20220901172606066

这里确保 只有获取锁的进程才能读取文件,没有锁的话直接退出,而且只有一把锁,可以防止进程启动多个副本,只有获得pid文件(固定路径固定文件名)写入权限(F_WRLCK)的进程才能正常启动并把自身的PID写入该文件中。其它同一个程序的多余进程则自动退出。

假如说我开了两个终端,都同时创建 osd,而且编号都相同,为了确保一个osd对应一个守护进程,跟据程序的设定,必须要读取pid文件才能继续执行,那谁先获得了 pid文件的读写锁,谁就可以继续执行;而且pid文件保存的是 进程的pid,所以可以通过文件来判断进程是否存在